﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace ScanSegmentSpecifyPort
{
    public partial class ScanIPFrm : Form
    {
        #region 声明变量

        /// <summary>
        /// 已扫描端口数目
        /// </summary>
        private double scannedCount = 0;

        /// <summary>
        /// 正在运行的线程数目
        /// </summary>
        private int runningThreadCount = 0;

        /// <summary>
        /// 最大工作线程数
        /// </summary>
        private static int maxThread = 100;

        /// <summary>
        /// IP地址
        /// </summary>
        private string host = null;

        /// <summary>
        /// 端口
        /// </summary>
        private int port = 1234;

        private int startIP = 1;
        private int endIP = 255;
        private string addresIP = "192.168.1.";

        public static Point CPoint;

        #endregion 声明变量

        #region 窗体方法

        public ScanIPFrm()
        {
            InitializeComponent();
        }

        private void ScanIPFrm_Load(object sender, EventArgs e)
        {
            //this.Text = string.Format("扫描IP段指定端口 {0}-{1}:{2}",addresIP + startIP, endIP, port);
            label2.Text = string.Format("扫描IP段指定端口 {0}-{1}:{2}", addresIP + startIP, endIP, port);
        }

        private void ScanIPFrm_MouseMove(object sender, MouseEventArgs e)
        {
            FrmMove(this, e);
        }

        private void listBox1_MouseMove(object sender, MouseEventArgs e)
        {
            FrmMove(this, e);
        }

        #region 利用窗体上的控件移动窗体

        /// <summary>
        /// 利用控件移动窗体
        /// </summary>
        /// <param Frm="Form">窗体</param>
        /// <param e="MouseEventArgs">控件的移动事件</param>
        public void FrmMove(ScanIPFrm Frm, MouseEventArgs e)  //CaseAmountConversionFrm或MouseEventArgs添加命名空间using System.Windows.Forms;
        {
            if (e.Button == MouseButtons.Left)
            {
                Point myPosittion = Control.MousePosition;//获取当前鼠标的屏幕坐标
                myPosittion.Offset(CPoint.X, CPoint.Y);//重载当前鼠标的位置
                Frm.DesktopLocation = myPosittion;//设置当前窗体在屏幕上的位置
            }
        }

        #endregion 利用窗体上的控件移动窗体

        #endregion 窗体方法

        #region 扫描方法

        /// <summary>
        /// 扫描
        /// </summary>
        /// <param name="m_host"></param>
        /// <param name="m_port"></param>
        public void Scan(string m_host, int m_port)
        {
            //我们直接使用比较高级的TcpClient类
            TcpClient tc = new TcpClient();
            //设置超时时间
            tc.SendTimeout = tc.ReceiveTimeout = 2000;

            try
            {
                //同步方法

                //IPAddress ip = IPAddress.Parse(host);
                //IPEndPoint IPendp = new IPEndPoint(ip, port);
                //tc.Connect(IPendp);

                //异步方法
                IAsyncResult oAsyncResult = tc.BeginConnect(m_host, m_port, null, null);
                oAsyncResult.AsyncWaitHandle.WaitOne(1000, true);//1000为超时时间

                if (tc.Connected)
                {
                    //如果连接上，证明此端口为开放状态
                    UpdateListBox(listBox1, m_host + ":" + m_port.ToString());
                }
            }
            catch (SocketException e)
            {
                //容错处理
                //MessageBox.Show("Port {0} is closed", host.ToString());
                //Console.WriteLine(e.Message);
            }
            finally
            {
                tc.Close();
                tc = null;
                scannedCount++;
                runningThreadCount--;
            }
        }

        #endregion 扫描方法

        #region 按钮事件

        /// <summary>
        /// 扫描
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            if (!backgroundWorker1.IsBusy)
            {
                listBox1.Items.Clear();
                scannedCount = 0;
                runningThreadCount = 0;
                backgroundWorker1.RunWorkerAsync();
            }
        }

        /// <summary>
        /// 取消
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

        /// <summary>
        /// 指定设备
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnChange_Click(object sender, EventArgs e)
        {
            SpecifiedDeviceFrm specifiedDeviceFrm = new SpecifiedDeviceFrm();
            specifiedDeviceFrm.SearcheEvent += new SpecifiedDeviceFrm.SearchDeletegate(frm_SearcheEvent);
            specifiedDeviceFrm.ShowDialog();
        }

        /// <summary>
        /// 关闭窗体
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void label1_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void frm_SearcheEvent(object sender, SearchEventArgs e)
        {
            startIP = Convert.ToInt32(e.SartIP.Split('.')[3]);
            endIP = Convert.ToInt32(e.EndIP.Split('.')[3]);

            addresIP = e.SartIP.Substring(0, e.SartIP.LastIndexOf('.') + 1);
            port = e.Port;
            //this.Text = string.Format("扫描IP段指定端口 {0}-{1}:{2}",addresIP + startIP, endIP, port);
        }

        #endregion 按钮事件

        #region 异步控件显示

        //Label
        private delegate void SetLabCallback(Label lb, string text);

        public void UpdateLabText(Label lb, string text)
        {
            try
            {
                if (lb.InvokeRequired)
                {
                    SetLabCallback d = new SetLabCallback(UpdateLabText);
                    this.Invoke(d, new object[] { lb, text });
                }
                else
                {
                    lb.Text = text.Trim();
                }
            }
            catch
            {
            }
        }

        //TextBox
        private delegate void SetTextCallback(TextBox txtBox, string text);

        private void UpdateTextBox(TextBox txtBox, string text)
        {
            if (txtBox.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(UpdateTextBox);
                this.Invoke(d, new object[] { txtBox, text });
            }
            else
            {
                txtBox.Text = text.Trim();
            }
        }

        //ListBox
        private delegate void SetListCallback(ListBox lstBox, string text);

        private void UpdateListBox(ListBox lstBox, string text)
        {
            if (lstBox.InvokeRequired)
            {
                SetListCallback d = new SetListCallback(UpdateListBox);
                this.Invoke(d, new object[] { lstBox, text });
            }
            else
            {
                lstBox.Items.Add(text.Trim());
            }
        }

        #endregion 异步控件显示

        #region 异步控件

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            double total = Convert.ToDouble(endIP - startIP + 1);
            for (int ip = startIP; ip <= endIP; ip++)
            {
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }

                //IP地址段，默认：192.168.1.
                host = addresIP + ip.ToString();

                //带参数的多线程执行
                Thread thread = new Thread(() => Scan(host, port));
                thread.IsBackground = true;
                thread.Start();

                UpdateLabText(labTip, string.Format("正在扫描第：{0}台，共{1}台，进度：{2}%",
                        scannedCount, total, Convert.ToInt32((scannedCount / total) * 100)));
                backgroundWorker1.ReportProgress(Convert.ToInt32((scannedCount / total) * 100));
                runningThreadCount++;

                Thread.Sleep(10);
                //循环，直到某个线程工作完毕才启动另一新线程，也可以叫做推拉窗技术
                while (runningThreadCount >= maxThread) ;
            }

            //空循环，直到所有端口扫描完毕
            do
            {
                UpdateLabText(labTip, string.Format("正在扫描第：{0}台，共{1}台，进度：{2}%",
                        scannedCount, total, Convert.ToInt32((scannedCount / total) * 100)));
                backgroundWorker1.ReportProgress(Convert.ToInt32((scannedCount / total) * 100));

                Thread.Sleep(10);
            } while (runningThreadCount > 0);
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            labTip.Text = "扫描完成！";
        }

        #endregion 异步控件
    }
}